﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.NumericalIntegration
{
    /// <summary>
    /// This class provides the numerical integration for integrals by using the trapezoid rule.
    /// </summary>
    [Serializable]
    public class TrapezoidIntegrator : AbstractSimpleIntegrator
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TrapezoidIntegrator"/> class.
        /// </summary>
        /// <param name="a">The lower value a of the integral.</param>
        /// <param name="b">The upper value b of the integral.</param>
        /// <param name="polynomial">The polynomial of the integral.</param>
        public TrapezoidIntegrator(int a, int b, Polynomial polynomial)
            : base(a, b, polynomial)
        {
        }

        /// <summary>
        /// Integrates the integral in a numerical way. After running the numerical integration
        /// it should be checked if the result has a precision error.
        /// </summary>
        /// <returns>The area of the specified integral.</returns>
        public override double Integrate()
        {
            bool swapBounds = false;

            if (this.B < this.A)
            {
                int tempuri = this.A;

                this.A = this.B;
                this.B = tempuri;
                swapBounds = true;
            }

            this.estimatedError = this.CalculateEstimatedError();

            if (swapBounds)
            {
                int tempuri = this.A;

                this.A = this.B;
                this.B = tempuri;

                return           -( ((this.B - this.A) / 2.0) * (this.Polynomial.SolveAt(this.A) + this.Polynomial.SolveAt(this.B)));

            }

            return ((this.B - this.A) / 2.0) * (this.Polynomial.SolveAt(this.A) + this.Polynomial.SolveAt(this.B));
        }

        /// <summary>
        /// Calculates the estimated error of the computed area of the integral.
        /// </summary>
        /// <returns>The estimated error of the computed area of the integral.</returns>
        private double CalculateEstimatedError()
        {
            double error = Double.MinValue;

            if (this.Polynomial.Degree >= 2)
            {
                Polynomial polynomial = this.Polynomial.Derivative().Derivative();

                for (int i = this.A; i <= this.B; i++)
                {
                    double tempuri = polynomial.SolveAt(i);

                    if (error < tempuri)
                    {
                        error = tempuri;
                    }
                }

                return (Math.Pow(this.B - this.A, 3) / 12) * error;
            }

            return 0;
        }
    }
}